package fileinn

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"os"
	"path/filepath"
	"time"
)

//文件类型
const (
	TypeFile string = "file"
	TypeDir  string = "dir"
)

// File 数据库文件对象
type File struct {
	Name        string //文件原名字
	Suffix      string
	Type        string    //文件类型/格式 file dir
	Hash        string    //文件哈希
	Path        string    //文件路径
	LateLog     string    // 最近一次的操作日志
	ExpiredTime time.Time //到期时间
	UpdateTime  time.Time //更新时间
	CreateTime  time.Time //创建时间
}

// SaveReader 保存文件✅
func SaveReader(src io.Reader, name string, t time.Duration) (*File, error) {
	var err error
	savefile := &File{}
	savefile.Name = name
	savefile.Type = TypeFile
	savefile.CreateTime = time.Now()
	savefile.UpdateTime = time.Now()
	savefile.ExpiredTime = time.Now().Add(t)
	savefile.LateLog = "保存文件"

	// 获取文件格式 后缀
	suffix, err := getSuffix(name)
	if err != nil {
		return savefile, err
	}
	savefile.Suffix = suffix

	//文件读取到Buffer
	fileBuffer, err := ioutil.ReadAll(src)
	if err != nil {
		return savefile, err
	}

	//获取文件哈希
	sha, err := SHA256File(bytes.NewBuffer(fileBuffer))
	if err != nil {
		return savefile, err
	}
	savefile.Hash = sha
	abspath, err := filepath.Abs(filepath.Join(SavePath, sha))
	if err != nil {
		return savefile, err
	}

	savefile.Path = abspath
	//创建保存文件
	out, err := os.OpenFile(savefile.Path, os.O_WRONLY|os.O_CREATE, os.FileMode(0666))
	if err != nil {
		return savefile, err
	}
	_, err = io.Copy(out, bytes.NewBuffer(fileBuffer))
	if err != nil {
		return savefile, err
	}
	defer out.Close()

	//插入数据库
	err = Insert(sha, savefile)
	if err != nil {
		return savefile, err
	}
	return savefile, nil
}

// SaveFilePath 保存文件✅
func SaveFilePath(src string, t time.Duration) (*File, error) {
	_, name := filepath.Split(src)
	var err error
	savefile := &File{}
	savefile.Name = name
	savefile.Type = TypeFile
	savefile.CreateTime = time.Now()
	savefile.UpdateTime = time.Now()
	savefile.ExpiredTime = time.Now().Add(t)
	savefile.LateLog = "保存文件"

	// 获取文件格式 后缀
	suffix, err := getSuffix(name)
	if err != nil {
		return savefile, err
	}
	savefile.Suffix = suffix

	//文件读取到Buffer
	fileBuffer, err := ioutil.ReadFile(src)
	if err != nil {
		return savefile, err
	}
	//获取文件哈希
	sha, err := SHA256File(bytes.NewBuffer(fileBuffer))
	if err != nil {
		return savefile, err
	}
	savefile.Hash = sha
	abspath, err := filepath.Abs(filepath.Join(SavePath, sha))
	if err != nil {
		return savefile, err
	}

	savefile.Path = abspath
	//创建保存文件
	out, err := os.OpenFile(savefile.Path, os.O_WRONLY|os.O_CREATE, os.FileMode(0666))
	if err != nil {
		return savefile, err
	}
	_, err = io.Copy(out, bytes.NewBuffer(fileBuffer))
	if err != nil {
		return savefile, err
	}
	defer out.Close()

	//插入数据库
	err = Insert(sha, savefile)
	if err != nil {
		return savefile, err
	}
	return savefile, nil
}

// SaveFileHeader 保存文件✅
func SaveFileHeader(src *multipart.FileHeader, t time.Duration) (*File, error) {
	savefile := &File{}
	savefile.Type = TypeFile
	savefile.Name = src.Filename
	savefile.CreateTime = time.Now()
	savefile.UpdateTime = time.Now()
	savefile.ExpiredTime = time.Now().Add(t)
	// 获取文件格式 后缀
	suffix, fixerr := getSuffix(src.Filename)
	if fixerr != nil {
		return savefile, fixerr
	}
	savefile.Suffix = suffix

	//打开文件去计算哈希以及保存
	file, operr := src.Open()
	if operr != nil {
		return savefile, operr
	}
	sha, shaerr := SHA256File(file)
	if shaerr != nil {
		return savefile, shaerr
	}
	savefile.Hash = sha
	_, seekerror := file.Seek(0, 0)
	if seekerror != nil {
		return savefile, seekerror
	}

	abspath, abserr := filepath.Abs(filepath.Join(SavePath, sha))
	if abserr != nil {
		return savefile, abserr
	}
	savefile.Path = abspath

	exist, ieerr := IsExist(savefile.Hash)
	if ieerr != nil {
		return savefile, ieerr
	}
	if exist {
		//如果文件存在
		savefile.LateLog = "重复上传/续期文件"
		upserr := Update(sha, savefile)
		if upserr != nil {
			return savefile, upserr
		}
	} else {
		//如果文件不存在 创建保存文件
		savefile.LateLog = "保存文件"
		out, err := os.OpenFile(savefile.Path, os.O_WRONLY|os.O_CREATE, os.FileMode(0666))
		if err != nil {
			return savefile, err
		}
		_, cperr := io.Copy(out, file)
		if cperr != nil {
			return savefile, cperr
		}
		defer out.Close()

		//插入数据库
		inserr := Insert(sha, savefile)
		if inserr != nil {
			return savefile, inserr
		}
	}

	return savefile, nil
}

// GetFile 获取文件
func GetFile(hash string) error {

	return nil
}

// Md5File 计算文件MD5
func Md5File(file io.Reader) (string, error) {
	h := md5.New()
	_, err := io.Copy(h, file)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

// SHA1File 计算文件的haxi1
func SHA1File(file io.Reader) (string, error) {
	h := sha1.New()
	_, err := io.Copy(h, file)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

// SHA256File 计算文件的haxi256
func SHA256File(file io.Reader) (string, error) {
	h := sha256.New()
	_, err := io.Copy(h, file)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

// getSuffix 通过文件名查找后缀
func getSuffix(file string) (string, error) {
	// 获取文件格式 后缀
	suffix := filepath.Ext(file)
	if suffix != "" {
		return suffix[1:], nil
	}
	return "", errors.New("文件名错误,无法获取文件格式/后缀")

}

// GetTempDir  创建并返回一个临时目录 到期后自动删除 /绝对目录
func GetTempDir(s string, t time.Duration) (string, error) {
	tmpPath, tmperr := ioutil.TempDir(SavePath, s)
	if tmperr != nil {
		return "", tmperr
	}
	_, name := filepath.Split(tmpPath)

	path, abserr := filepath.Abs(tmpPath)
	if abserr != nil {
		return "", abserr
	}

	savefile := &File{}
	savefile.Type = TypeDir
	savefile.Name = name
	savefile.Path = path
	savefile.CreateTime = time.Now()
	savefile.UpdateTime = time.Now()
	savefile.ExpiredTime = time.Now().Add(t)
	savefile.LateLog = "创建缓存目录"

	isterr := Insert(name, savefile)
	if isterr != nil {
		return "", isterr
	}

	return tmpPath, nil

}

// PathExists 判断目录是否存在
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
